package com.t360.cubesolver;

import java.util.ArrayList;
import java.util.HashMap;

import com.t360.cubesolver.Solution.Position;

/**
 * It models the puzzle as set of pieces
 * @author fmanganiello
 *
 */
public class Puzzle {
	private ArrayList<PuzzlePiece> pieces;

	public Puzzle(int size) {
		pieces = new ArrayList<PuzzlePiece>(size);
	}
	
	/**
	 * Set a piece at the position <i> in the puzzle
	 * @param i
	 * @param piece
	 */
	public void set(int i, PuzzlePiece piece) {
		pieces.add(i, piece);
	}
	
	/**
	 * The puzzle will be solved placing the pieces in the following order:
	 * TOP_LEFT -> TOP_MID -> TOP_RIGHT -> BOTTOM_TOP -> BOTTOM_MID -> BOTTOM_BOTTOM
	 * Given a certain position, this method returns the next position to be filled
	 * @param position
	 * @return
	 */
	private static Position getNextPosition(Position position) {
		if (position.equals(Position.TOP_LEFT)) {
			return Position.TOP_MID;
		}
		else if (position.equals(Position.TOP_MID)) {
			return Position.TOP_RIGHT;
		}
		else if (position.equals(Position.TOP_RIGHT)) {
			return Position.BOTTOM_TOP;
		}
		else if (position.equals(Position.BOTTOM_TOP)) {
			return Position.BOTTOM_MID;
		}
		else if (position.equals(Position.BOTTOM_MID)) {
			return Position.BOTTOM_BOTTOM;
		}
		else {
			return Position.TOP_LEFT;
		}
	}
	
	/**
	 * It solves the puzzle, returning all the possible solution in a hash map
	 * @param solutions Hash map <String, Solution> that will contain all the solutions.
	 * The key is the string representation of the solution, in order to prevent duplicated
	 * entries, the value is the actual Solution object
	 * @param curSolution Current solution (used for the recursive call)
	 * @param curPosition Current position in the solution (used for the recursive call)
	 */
	private void solve(final HashMap<String, Solution> solutions, final Solution curSolution, final Position curPosition) {
		// Iterate on each piece of the puzzle
		for (int i=0; i < pieces.size(); ++i) {
			// Go to the next piece if the solution already contains this piece
			PuzzlePiece piece = pieces.get(i).clone();
			if (curSolution.contains(piece)) {
				continue;
			}
			
			// Iterate on all the possible combinations of rotations and flips
			for (int flip=0; flip < 2; ++flip) {
				for (int rotation=0; rotation < 4; ++rotation) {
					// Try to set the piece on the current position
					curSolution.set(curPosition, piece);
					
					if (curSolution.isValid()) {
						// If all the constraints are still respected placing the piece here,
						// and this is the last piece to be placed (BOTTOM_BOTTOM), then I have
						// found a solution. Add it to the hash map
						if (curPosition.equals(Position.BOTTOM_BOTTOM)) {
							solutions.put(curSolution.toString(), curSolution.clone());
						}
						else {
							// Recursive call
							solve(solutions, curSolution, getNextPosition(curPosition));
						}
					}
					
					curSolution.remove(piece);
					piece.rotateLeft();
				}
				
				piece.flip();
			}
		}
	}
	
	// Method to be invoked from the external world (it hides the parameters internally used for the recursive call)
	public void solve(HashMap<String, Solution> solutions) {
		solve(solutions, new Solution(), Position.TOP_LEFT);
	}
}
